package com.jichangxiu.common.entity.bo;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jichangxiu.common.utils.ObjUtils;
import com.jichangxiu.common.utils.StrUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;

public class Record extends LinkedHashMap<String, Object> implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final String RECORD_KEY = "RECORD_KEY";

    public static final String CURRENT_REQUEST_RECORD = "CURRENT_REQUEST_RECORD";

    public Record() {
    }

    public Record(Map<String, Object> map) {
        this.setMap(map);
    }

    @Override
    public Object get(Object key) {
        if (ObjUtils.equals(key, "this") || ObjUtils.equals(key, "THIS")) return this;
        return super.get(key);
    }

    public Object get(Object key, Object defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return value;
    }

    public String getStr(String key) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return null;
        return String.valueOf(value);
    }

    public String getStr(String key, String defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return String.valueOf(value);
    }

    public int getInt(String key) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return 0;
        return Integer.parseInt(String.valueOf(value));
    }

    public int getInt(String key, int defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return Integer.parseInt(String.valueOf(value));
    }

    public long getLong(String key) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return 0;
        return Long.parseLong(String.valueOf(value));
    }

    public long getLong(String key, long defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return Long.parseLong(String.valueOf(value));
    }

    public double getDouble(String key) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return 0.0;
        return Double.parseDouble(String.valueOf(value));
    }

    public double getDouble(String key, double defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return Double.parseDouble(String.valueOf(value));
    }

    public Boolean getBoolean(String key) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return false;
        return Boolean.parseBoolean(String.valueOf(value));
    }

    public boolean getBoolean(String key, boolean defaultValue) {
        Object value = get(key);
        if (ObjectUtil.isEmpty(value)) return defaultValue;
        return Boolean.parseBoolean(String.valueOf(value));
    }

    public Date getDateByFormat(String key, String format) {
        try {
            return new SimpleDateFormat(format).parse(getStr(key));
        } catch (Exception ex) {
            throw new RuntimeException("【Record】时间格式化异常", ex);
        }
    }

    public Date getDate(String key) {
        return getDateByFormat(key, "yyyy-MM-dd");
    }

    public Date getDateTime(String key) {
        return getDateByFormat(key, "yyyy-MM-dd HH:mm:ss");
    }

    public List<Object> getList(String key) {
        Object value = get(key);
        List<Object> list;
        if (ObjectUtil.isEmpty(value))
            list = new ArrayList<>();
        else if (value instanceof List)
            list = (List<Object>) value;
        else
            list = ListUtil.toList(value);
        return list;
    }

    public <T> List<T> getList(String key, Class<T> cs) {
        List<Object> list = getList(key);
        List<T> list2 = new ArrayList<>();
        for (Object obj : list) {
            T objC = ObjUtils.getValueByClass(obj, cs);
            list2.add(objC);
        }
        return list2;
    }

    public <T> List<T> getListByComma(String key, Class<T> cs) {
        String listStr = getStr(key);
        if (StrUtil.isEmpty(listStr))
            return new ArrayList<>();
        // 开始转化
        String[] arr = listStr.split(",");
        List<T> list = new ArrayList<T>();
        for (String str : arr) {
            if (cs == int.class || cs == Integer.class || cs == long.class || cs == Long.class)
                str = str.trim();
            T objC = ObjUtils.getValueByClass(str, cs);
            list.add(objC);
        }
        return list;
    }

    public <T> T getModel(Class<T> cs) {
        try {
            return getModelByObject(cs.newInstance());
        } catch (Exception ex) {
            throw new RuntimeException("【Record】获取模型数据异常", ex);
        }
    }

    private <T> T getModelByObject(T obj) {
        // 获取类型
        Class<?> cs = obj.getClass();
        // 循环复制
        for (Field field : cs.getDeclaredFields()) {
            try {
                // 获取对象
                Object value = this.get(field.getName());
                if (ObjectUtil.isEmpty(value)) continue;
                field.setAccessible(true);
                Object valueConvert = ObjUtils.getValueByClass(value, field.getType());
                field.set(obj, valueConvert);
            } catch (IllegalArgumentException | IllegalAccessException ex) {
                throw new RuntimeException("【Record】获取模型数据异常", ex);
            }
        }
        return obj;
    }

    public <T> T getModel(String key, Class<T> cs) {
        try {
            return ObjUtils.getValueByClass(this.get(key), cs);
        } catch (Exception ex) {
            throw new RuntimeException("【Record】获取模型数据异常", ex);
        }
    }

    public Record set(String key, Object value) {
        if ("this".equalsIgnoreCase(key))
            return this;
        put(key, value);
        return this;
    }

    public void setDefaultValue(String key, Object defaultValue) {
        if (StrUtil.isNotEmpty(key))
            set(key, defaultValue);
    }

    public Record setMap(Map<String, ?> map) {
        if (ObjectUtil.isNotEmpty(map))
            for (String key : map.keySet())
                this.set(key, map.get(key));
        return this;
    }

    public Record setModel(Object model) {
        if (ObjectUtil.isEmpty(model)) return Record.getRecord();
        if (model instanceof Record) return (Record) model;
        if (model instanceof String)
            this.set(RECORD_KEY, model);
        else {
            List<Field> fieldList = ObjUtils.reflectForField(model.getClass());
            if (ObjectUtil.isNotEmpty(fieldList)) {
                for (Field field : fieldList) {
                    try {
                        field.setAccessible(true);
                        boolean isStatic = Modifier.isStatic(field.getModifiers());
                        if (!isStatic) this.set(field.getName(), field.get(model));
                    } catch (Exception ex) {
                        throw new RuntimeException("【Record】设置模型数据异常", ex);
                    }
                }
            }
        }
        return this;
    }

    public Record setJsonStr(String jsonStr) {
        try {
            Map map = new ObjectMapper().readValue(jsonStr, Map.class);
            return this.setMap(map);
        } catch (JsonProcessingException ex) {
            throw new RuntimeException("【Record】设置模型数据异常", ex);
        }
    }

    public Record delete(String key) {
        remove(key);
        return this;
    }

    public Record clearNull() {
        Iterator<String> iterator = this.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (StrUtil.isEmpty(key)) {
                iterator.remove();
                this.remove(key);
            }
        }
        return this;
    }

    public Record clearIn(String... keys) {
        List<String> keys2 = Arrays.asList(keys);
        Iterator<String> iterator = this.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (keys2.contains(key)) {
                iterator.remove();
                this.remove(key);
            }
        }
        return this;
    }

    public Record clearNotIn(String... keys) {
        List<String> keys2 = Arrays.asList(keys);
        Iterator<String> iterator = this.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            if (!keys2.contains(key)) {
                iterator.remove();
                this.remove(key);
            }
        }
        return this;
    }

    public Record clearAll() {
        clear();
        return this;
    }

    public static Record getRecord() {
        return new Record();
    }

    public static Record getRecord(String key, Object value) {
        return new Record().set(key, value);
    }

    public static Record getRecord(Map<String, ?> map) {
        return new Record().setMap(map);
    }

    public static Record getRecordByModel(Object model) {
        return Record.getRecord().setModel(model);
    }

    public static List<Record> getRecordByList(List<?> list) {
        List<Record> listMap = new ArrayList<>();
        for (Object model : list) {
            if (model instanceof Record)
                listMap.add((Record) model);
            else
                listMap.add(getRecordByModel(model));
        }
        return listMap;
    }

    public Record cloneKeys(String... keys) {
        Record record = new Record();
        for (String key : keys)
            record.set(key, this.get(key));
        return record;
    }

    public Record cloneRecord() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(key, this.get(key));
        return record;
    }

    public Record toUpperCase() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(key.toUpperCase(), this.get(key));
        this.clearAll().setMap(record);
        return this;
    }

    public Record toLowerCase() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(key.toLowerCase(), this.get(key));
        this.clearAll().setMap(record);
        return this;
    }

    public Record toKebabCase() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(StrUtils.wordEachKebabCase(key), this.get(key));
        this.clearAll().setMap(record);
        return this;
    }

    public Record toHumpCase() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(StrUtils.wordEachBigFs(key), this.get(key));
        this.clearAll().setMap(record);
        return this;
    }

    public Record humpToLineCase() {
        Record record = new Record();
        for (String key : this.keySet())
            record.set(StrUtils.wordHumpToLine(key), this.get(key));
        this.clearAll().setMap(record);
        return this;
    }

    public boolean isNumber(String key) {
        String value = getStr(key);
        if (StrUtil.isEmpty(value))
            return false;
        return Pattern.compile("[0-9]*").matcher(value).matches();
    }

    public String toJsonStr() {
        try {
            return new ObjectMapper().writeValueAsString(this);
        } catch (Exception ex) {
            throw new RuntimeException("【Record】JSON转换异常", ex);
        }
    }

    public String toJsonFormatStr() {
        try {
            return JSONUtil.toJsonStr(this);
        } catch (Exception ex) {
            throw new RuntimeException("【Record】JSON格式化异常", ex);
        }
    }

    public static Record getRequestRecord() {
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (servletRequestAttributes == null)
            throw new RuntimeException("【Record】非JavaWeb环境异常");
        HttpServletRequest request = servletRequestAttributes.getRequest();
        if (request.getAttribute(CURRENT_REQUEST_RECORD) == null || !(request.getAttribute(CURRENT_REQUEST_RECORD) instanceof Record))
            initRequestRecord(request);
        return (Record) request.getAttribute(CURRENT_REQUEST_RECORD);
    }

    public static Record getRequestRecord(HttpServletRequest request) {
        if (request.getAttribute(CURRENT_REQUEST_RECORD) == null || !(request.getAttribute(CURRENT_REQUEST_RECORD) instanceof Record))
            initRequestRecord(request);
        return (Record) request.getAttribute(CURRENT_REQUEST_RECORD);
    }

    private static void initRequestRecord(HttpServletRequest request) {
        Record record;
        // 获取所有参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        if (!parameterMap.isEmpty()) {
            record = new Record();
            for (String key : parameterMap.keySet()) {
                try {
                    // 获得values
                    String[] values = parameterMap.get(key);
                    if (values.length == 1) {
                        record.set(key, values[0]);
                    } else {
                        List<String> list = new ArrayList<>(Arrays.asList(values));
                        record.set(key, list);
                    }
                } catch (Exception ex) {
                    throw new RuntimeException("【Record】初始化请求参数异常", ex);
                }
            }
        } else {
            try {
                String jsonStr = IoUtil.readUtf8(request.getInputStream());
                if (StrUtil.isNotEmpty(jsonStr)) {
                    record = JSONUtil.toBean(jsonStr, Record.class);
                } else {
                    record = new Record();
                }
            } catch (Exception ex) {
                ex.printStackTrace();
                record = new Record();
            }
        }
        request.setAttribute(CURRENT_REQUEST_RECORD, record);
    }

    public static List<Record> listToTree(List<Record> list, String idKey, String parentIdKey, String childListKey) {
        // 声明新的集合，存储tree形数据
        List<Record> newTreeList = new ArrayList<>();
        // 声明hash-Map，方便查找数据
        Record hash = new Record();
        // 将数组转为Object的形式，key为数组中的id
        for (Record json : list)
            hash.put(json.getStr(idKey), json);
        // 遍历结果集
        for (Record aVal : list) {
            // 单条记录
            // 在hash中取出key为单条记录中pid的值
            Record hashVp = (Record) hash.get(aVal.get(parentIdKey, "").toString());
            // 如果记录的pid存在，则说明它有父节点，将她添加到孩子节点的集合中
            if (hashVp != null) {
                // 检查是否有child属性，有则添加，没有则新建
                if (hashVp.get(childListKey) != null) {
                    @SuppressWarnings("unchecked") List<Record> ch = (List<Record>) hashVp.get(childListKey);
                    ch.add(aVal);
                    hashVp.put(childListKey, ch);
                } else {
                    List<Record> ch = new ArrayList<>();
                    ch.add(aVal);
                    hashVp.put(childListKey, ch);
                }
            } else
                newTreeList.add(aVal);
        }
        return newTreeList;
    }

    public static List<Record> pruneTree(List<Record> treeList, String childListKey, String key, String value) {
        List<Record> prunedList = new ArrayList<>();
        for (Record node : treeList) {
            // 当前节点符合条件，
            if (node.getStr(key).equals(value)) continue;
            // 更新当前节点的子集合为裁剪后的子集合
            node.put(childListKey, pruneTree(node.getList(childListKey, Record.class), childListKey, key, value));
            prunedList.add(node);
        }
        return prunedList;
    }

    public static List<Record> findTreeNodePath(List<Record> treeList, String childListKey, String key, String value) {
        List<Record> nodePath = new ArrayList<>();
        findTreeNodePathRecursive(treeList, childListKey, key, value, nodePath);
        // 将链路反转，使根节点在第一个位置
        Collections.reverse(nodePath);
        return nodePath;
    }

    private static boolean findTreeNodePathRecursive(List<Record> nodes, String childListKey, String key, String value, List<Record> nodePath) {
        for (Record node : nodes) {
            // 当前节点符合条件
            if (node.getStr(key).equals(value)) {
                // 找到目标节点，将其加入链路并返回 true
                nodePath.add(node);
                return true;
            } else {
                // 继续递归查找子节点
                if (findTreeNodePathRecursive(node.getList(childListKey, Record.class), childListKey, key, value, nodePath)) {
                    // 如果找到目标节点，则将当前节点加入链路并返回 true
                    nodePath.add(node);
                    return true;
                }
            }
        }
        // 当前节点及其子节点中没有目标节点
        return false;
    }

}
